package com.jt.utils;



import org.apache.commons.lang3.StringUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlException;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHpsMeasure;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
import org.springframework.mock.web.MockMultipartFile;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

public class Base64DocumentTemplateUtil {


    /**
     * 从Base64编码的字符串表示的文档模板中，根据占位符替换内容（文本和Base64图片），并返回处理后的MockMultipartFile对象
     *
     * @param base64Template Base64编码的文档模板字符串，代表整个文档经过编码后的形式，后续通过解码、解析来进行占位符替换等操作
     * @param placeholderMap 占位符与对应替换内容的映射，键为占位符（如${name}），值为替换文本或者Base64编码的图片数据列表（用List<String>表示多张图片），定义了文档中各占位符的替换内容
     * @param fileName       输出的MockMultipartFile的文件名，用于设置模拟文件的名称，应包含正确的文件扩展名，例如 "output.docx"
     * @return MockMultipartFile对象，经过占位符替换及图片插入等操作后的文档内容被封装在此对象中，可用于模拟文件上传等场景
     * @throws IOException 如果在Base64解码、文档处理、文件流读写等操作过程中出现I/O相关异常则抛出
     */
    public static MockMultipartFile replacePlaceholdersAndGetMockMultipartFile(String base64Template, Map<String, Object> placeholderMap, String fileName) throws IOException {
        return replacePlaceholdersAndGetMockMultipartFile(base64Template, placeholderMap, fileName, d -> {
        });
    }

    /**
     * 从Base64编码的字符串表示的文档模板中，根据占位符替换内容（文本和Base64图片），并返回处理后的MockMultipartFile对象
     *
     * @param base64Template Base64编码的文档模板字符串，代表整个文档经过编码后的形式，后续通过解码、解析来进行占位符替换等操作
     * @param placeholderMap 占位符与对应替换内容的映射，键为占位符（如${name}），值为替换文本或者Base64编码的图片数据列表（用List<String>表示多张图片），定义了文档中各占位符的替换内容
     * @param fileName       输出的MockMultipartFile的文件名，用于设置模拟文件的名称，应包含正确的文件扩展名，例如 "output.docx"
     * @return MockMultipartFile对象，经过占位符替换及图片插入等操作后的文档内容被封装在此对象中，可用于模拟文件上传等场景
     * @throws IOException 如果在Base64解码、文档处理、文件流读写等操作过程中出现I/O相关异常则抛出
     */
    public static MockMultipartFile replacePlaceholdersAndGetMockMultipartFile(String base64Template, Map<String, Object> placeholderMap, String fileName, Consumer<XWPFDocument> slot) throws IOException {
        // 将Base64编码的文档模板字符串解码为字节数组，以便后续转换为输入流来读取文档内容
        byte[] decodedBytes = Base64.getDecoder().decode(base64Template);
        ByteArrayInputStream bis = new ByteArrayInputStream(decodedBytes);
        // 使用Apache POI库解析Word文档，得到文档对象，后续所有操作都基于这个对象进行
        XWPFDocument document = new XWPFDocument(bis);
        // 调用方法替换文档中的文本占位符
        replaceTextPlaceholders(document, placeholderMap);
        // 调用方法替换文档中的图片占位符，将Base64编码的图片插入文档中
        replaceImagePlaceholders(document, placeholderMap);
        slot.accept(document);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        // 将修改后的文档内容写入字节数组输出流
        document.write(bos);

//        File docxFile = FileUtil.file("D:/测试文件.docx");
//        FileOutputStream fos = new FileOutputStream(docxFile);
//        document.write(fos);
//        fos.close();

        // 关闭文档对象，释放其占用的资源
        document.close();
        bis.close();
        // 获取处理后的文档内容字节数组
        byte[] processedBytes = bos.toByteArray();
        // 使用处理后的字节数组创建InputStream，作为MockMultipartFile构造函数的参数
        InputStream inputStream = new ByteArrayInputStream(processedBytes);
        // 创建MockMultipartFile对象，设置文件名、原始文件名、内容类型以及输入流等参数
        MockMultipartFile mockMultipartFile = new MockMultipartFile(
                fileName,  // 文件名称
                fileName,  // 原始文件名（这里简单设置和文件名相同，可根据实际需求调整）
                "application/vnd.openxmlformats-officedocument.wordprocessingml.document",  // 文档类型（以Word文档为例，可根据实际文档类型修改）
                inputStream);
        return mockMultipartFile;
    }


    /**
     * 替换文档中的文本占位符的私有方法
     *
     * @param document
     * @param placeholderMap
     */
    private static void replaceTextPlaceholders(XWPFDocument document, Map<String, Object> placeholderMap) {
        // 遍历文档中的所有段落（Paragraph），段落是Word文档中文字排版的基本单位
        for (XWPFParagraph paragraph : document.getParagraphs()) {
            // 获取段落中的所有文本运行（Run），一个Run可以理解为一段具有相同格式的连续文本
            List<XWPFRun> runs = paragraph.getRuns();
            for (XWPFRun run : runs) {
                // 获取当前Run中的文本内容
                String text = run.getText(0);
                if (text != null) {
                    // 遍历占位符映射表，查找文本占位符并进行替换
                    for (Map.Entry<String, Object> entry : placeholderMap.entrySet()) {
                        // 只处理值为字符串类型的占位符（即文本占位符），忽略图片占位符相关的映射
                        if (entry.getValue() instanceof String) {
                            text = text.replace(entry.getKey(), (String) entry.getValue());
                        }
                    }
                    // 将替换后的文本重新设置回当前Run中
                    run.setText(text, 0);
                }
            }
        }

        // 遍历文档中的所有表格（Table）
        for (XWPFTable table : document.getTables()) {
            // 遍历表格中的每一行（TableRow）
            for (XWPFTableRow row : table.getRows()) {
                // 遍历行中的每一个单元格（TableCell）
                for (XWPFTableCell cell : row.getTableCells()) {
                    // 获取单元格中的所有段落
                    List<XWPFParagraph> paragraphs = cell.getParagraphs();
                    for (XWPFParagraph paragraph : paragraphs) {
                        List<XWPFRun> runs = paragraph.getRuns();
                        for (XWPFRun run : runs) {
                            String text = run.getText(0);
                            if (text != null) {
                                if ("${seizeASeat}".equals(text)) {
                                    List<String> ItemNames = (List<String>) placeholderMap.getOrDefault("${seizeASeat}", new ArrayList<>());
                                    String letterLevel = placeholderMap.get("${letterLevel}").toString();
                                    int size = ItemNames.size();
                                    run.setText("", 0);
                                    run.setFontFamily("宋体");
                                    run.setFontSize(12);
                                    // 创建复选框
                                    for (int i = 0; i < size; i++) {
                                        String itemName = ItemNames.get(i);
                                        if (letterLevel.equals(itemName)) {
                                            run.setText("■" + itemName);
                                        } else {
                                            run.setText("□" + itemName);
                                        }
                                        if (i != size - 1) {
                                            run.addTab();
                                        }
                                    }
                                } else {
                                    for (Map.Entry<String, Object> entry : placeholderMap.entrySet()) {
                                        if (entry.getValue() instanceof String) {
                                            text = text.replace(entry.getKey(), (String) entry.getValue());
                                        }
                                    }
                                    run.setText(text, 0);
                                }
                            }
                        }
                    }
//                    cell.getParagraphs().forEach(p -> {
//                                String replaceText = p.getText();
//                                List<XWPFRun> runs = p.getRuns();
//                                if (StringUtils.contains(replaceText, "@{")) {
//                                    runs.forEach(r -> {
//                                        if (r.getText(0).contains("@{")) {
//                                            String param = StringUtils.substringBetween(r.getText(0), "@{", "}");
//                                            List<Map<String, String>> params = (List<Map<String, String>>) placeholderMap.get("${" + param + "}");
//                                            List<Object> methodDiagramIds = (List<Object>) placeholderMap.get("${methodDiagramIds}");
//                                            List<Map<String, String>> str = DimensionChanger.obtainTheActualData(params, methodDiagramIds);
//                                            r.setText("", 0);
//                                            double[] scaleFactor = DimensionChanger.adjustDimensions(str, cell.getCTTc().getTcPr().getTcW().getW().intValue(), row.getHeight() / 2);
//                                            for (Map<String, String> stringStringMap : str) {
//                                                try {
//                                                    DimensionChanger.drawAPicture(r, stringStringMap, scaleFactor);
//                                                } catch (IOException | InvalidFormatException e) {
//                                                    throw new RuntimeException(e);
//                                                }
//                                            }
//                                        }
//                                    });
//                                }
//                            }
//                    );
                }
            }
        }
    }

    /**
     * 替换文档中的图片占位符的私有方法，实现插入多张Base64编码图片的功能
     *
     * @param document
     * @param placeholderMap
     */
    private static void replaceImagePlaceholders(XWPFDocument document, Map<String, Object> placeholderMap) {
        // 遍历文档中的所有段落
        for (XWPFParagraph paragraph : document.getParagraphs()) {
            List<XWPFRun> runs = paragraph.getRuns();
            for (int i = 0; i < runs.size(); i++) {
                XWPFRun run = runs.get(i);
                String text = run.getText(0);
                if (text != null) {
                    for (Map.Entry<String, Object> entry : placeholderMap.entrySet()) {
                        // 只处理值为Base64编码图片数据列表类型的占位符（即图片占位符）
                        if (entry.getValue() instanceof List) {
                            List<String> base64ImageList = (List<String>) entry.getValue();
                            if (text.contains(entry.getKey())) {
                                for (String base64Image : base64ImageList) {
                                    try {
                                        // 将Base64编码的图片数据解码为字节数组
                                        byte[] imageBytes = Base64.getDecoder().decode(base64Image);
                                        ByteArrayInputStream imageStream = new ByteArrayInputStream(imageBytes);

                                        // 获取图片格式，这里简单通过判断字节数组开头的文件头信息来初步判断格式（可进一步优化）
                                        int format;
                                        if (imageBytes[0] == (byte) 0x89 && imageBytes[1] == (byte) 0x50 && imageBytes[2] == (byte) 0x4E && imageBytes[3] == (byte) 0x47) {
                                            format = XWPFDocument.PICTURE_TYPE_PNG;
                                        } else if (imageBytes[0] == (byte) 0xFF && imageBytes[1] == (byte) 0xD8 && imageBytes[2] == (byte) 0xFF) {
                                            format = XWPFDocument.PICTURE_TYPE_JPEG;
                                        } else {
                                            continue;
                                        }

                                        // 将图片添加到当前段落的当前Run中，设置图片尺寸等信息（这里尺寸暂设为固定值，可按需优化）
                                        try {
                                            run.addPicture(imageStream, format, "", Units.toEMU(200), Units.toEMU(150));
                                        } catch (InvalidFormatException e) {
                                            e.printStackTrace();
                                        }
                                        imageStream.close();
                                    } catch (IOException e) {
                                        e.printStackTrace();
                                    }
                                }
                                // 移除原来包含占位符的文本内容，因为已经插入了对应的图片
                                run.setText("", 0);
                            }
                        }
                    }
                }
            }
        }

        // 遍历文档中的所有表格
        for (XWPFTable table : document.getTables()) {
            for (XWPFTableRow row : table.getRows()) {
                for (XWPFTableCell cell : row.getTableCells()) {
                    List<XWPFParagraph> paragraphs = cell.getParagraphs();
                    for (XWPFParagraph paragraph : paragraphs) {
                        List<XWPFRun> runs = paragraph.getRuns();
                        for (int i = 0; i < runs.size(); i++) {
                            XWPFRun run = runs.get(i);
                            String text = run.getText(0);
                            if (text != null) {
                                for (Map.Entry<String, Object> entry : placeholderMap.entrySet()) {
                                    if (entry.getValue() instanceof List) {
                                        List<String> base64ImageList = (List<String>) entry.getValue();
                                        if (text.contains(entry.getKey())) {
                                            for (String base64Image : base64ImageList) {
                                                try {
                                                    byte[] imageBytes = Base64.getDecoder().decode(base64Image);
                                                    ByteArrayInputStream imageStream = new ByteArrayInputStream(imageBytes);
                                                    int format;
                                                    if (imageBytes[0] == (byte) 0x89 && imageBytes[1] == (byte) 0x50 && imageBytes[2] == (byte) 0x4E && imageBytes[3] == (byte) 0x47) {
                                                        format = XWPFDocument.PICTURE_TYPE_PNG;
                                                    } else if (imageBytes[0] == (byte) 0xFF && imageBytes[1] == (byte) 0xD8 && imageBytes[2] == (byte) 0xFF) {
                                                        format = XWPFDocument.PICTURE_TYPE_JPEG;
                                                    } else {
                                                        continue;
                                                    }

                                                    try {
                                                        imageStream = new ByteArrayInputStream(imageBytes);
                                                        run.addPicture(imageStream, format, "", Units.toEMU(placeholderMap.get("${tableImgPoints1}") != null ? (double) placeholderMap.get("${tableImgPoints1}") : 900), Units.toEMU(placeholderMap.get("${tableImgPoints2}") != null ? (double) placeholderMap.get("${tableImgPoints2}") : 1800));
                                                    } catch (InvalidFormatException e) {
                                                        e.printStackTrace();
                                                    }
                                                    imageStream.close();
                                                } catch (IOException e) {
                                                    e.printStackTrace();
                                                }
                                            }
                                            run.setText("", 0);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

//    private static void setCellValue(int startColumn, String[] fields, int fieldLength, Bean record, XWPFTableRow row) {
//        List<XWPFTableCell> tableCells = row.getTableCells();
//        for (int index = 0; index < fieldLength && index + startColumn < tableCells.size(); index++) {
//            String field = fields[index];
//            XWPFTableCell cell = tableCells.get(index + startColumn);
//            Object o = record.get(field);
////            if (o instanceof CellImageAndTextDTO) {
////                dealImageTextCell((CellImageAndTextDTO) o, cell);
////            } else {
//            XWPFParagraph paragraph = cell.getParagraphs().get(0);
//            int size = paragraph.getRuns().size();
//            for (int icc = 0; icc < size; icc++) {
//                paragraph.removeRun(0);
//            }
//            String recordStr = record.getStr(field);
//            // paragraph.createRun().setText(recordStr);
//            XWPFRun run = paragraph.createRun();
//            // 替代run.setFontSize()由于五号字体为10.5，这个方法只能接收整数，但实际word的xml文件中为该值的二倍因此重写了该方法
//            BigInteger bint = new BigInteger(String.valueOf((int) (10.5 * 2)));
//            CTR ctr = run.getCTR();
//            CTRPr pr = ctr.isSetRPr() ? ctr.getRPr() : null;
//            if (pr == null) {
//                pr = ctr.addNewRPr();
//            }
//            CTHpsMeasure ctSize = pr.isSetSz() ? pr.getSz() : pr.addNewSz();
//            ctSize.setVal(bint);
//            run.setFontFamily("宋体");
//            run.setText(recordStr);
////            }
//        }
//    }
//
//    public static void setTableDate(String[] fields, List<Bean> dataList, XWPFTable table, int startRow, int startColumn) {
//        List<XWPFTableRow> rows = table.getRows();
//        XWPFTableRow copyRow = table.getRow(startRow);
//        CTRow ctrow = null;
//        try {
//            ctrow = CTRow.Factory.parse(copyRow.getCtRow().newInputStream());
//        } catch (XmlException | IOException e) {
//            e.printStackTrace();
//        }
//        int fieldLength = fields.length;
//        //遍历数据集，当表格不够时则新建行插入，否则正常插入
//        for (int i = 0; i < dataList.size() && i < rows.size(); i++) {
//            Bean record = dataList.get(i);
//            XWPFTableRow row;
//            if (i + startRow - 1 > 0 && (rows.get(i + startRow - 1).getTableCells().size() != rows.get(i + startRow).getTableCells().size())) {
//                row = new XWPFTableRow(ctrow, table);
//                setCellValue(startColumn, fields, fieldLength, record, row);
//                table.addRow(row, i + startRow);
//            } else {
//                row = table.getRow(i + startRow); //获取行
//                setCellValue(startColumn, fields, fieldLength, record, row);
//            }
//        }
//    }
}
